package com.dynamo.transport.network.practise.channel;

import java.util.HashMap;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * @author duanyimiao
 * @create 2018-11-17 10:08 AM
 * @description
 **/
public class DefaultChannelPipeline implements ChannelPipeline {
    //链表头
    private DefaultChannelHandlerContext head;
    //链表尾
    private DefaultChannelHandlerContext tail;
    //存放hadnler name和handler映射
    private Map<String, DefaultChannelHandlerContext> name2ctx = new HashMap<>(4);
    private ChannelSink sink;
    private Channel channel;

    @Override
    public void addFirst(String name, ChannelHandler handler) {
        if (name2ctx.isEmpty()) {
            init(name, handler);
            return;
        }
        checkDuplicateName(name);
        DefaultChannelHandlerContext oldHead = head;
        DefaultChannelHandlerContext newHead = new DefaultChannelHandlerContext(null, oldHead, name, handler);

        oldHead.prev = newHead;
        head = newHead;
        name2ctx.put(name, newHead);
    }

    @Override
    public void addLast(String name, ChannelHandler handler) {
        if (name2ctx.isEmpty()) {
            init(name, handler);
            return;
        }
        checkDuplicateName(name);
        DefaultChannelHandlerContext oldTail = tail;
        DefaultChannelHandlerContext newTail = new DefaultChannelHandlerContext(oldTail, null, name, handler);

        oldTail.next = newTail;
        tail = newTail;
        name2ctx.put(name, newTail);
    }

    @Override
    public void addBefore(String oldName, String newName, ChannelHandler newHandler) {
       /* DefaultChannelHandlerContext oldCtx = getContext(oldName);
        if (oldCtx == null) {
            throw new NoSuchElementException("addBefore no such element oldName " + oldName);
        }
        将此段代码extract 到 getContextOrException()中，检测是否存在在ctx放在该方法进行
        */
        DefaultChannelHandlerContext oldCtx = getContextOrException(oldName);
        doAddBefore(oldCtx, newName, newHandler);
    }

    @Override
    public void addBefore(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
        /*DefaultChannelHandlerContext oldCtx = getContext(oldHandler);
        if (oldCtx == null) {
            throw new NoSuchElementException("addBefore no such element oldHandler " + oldHandler);
        }*/
        DefaultChannelHandlerContext oldCtx = getContextOrException(oldHandler);
        doAddBefore(oldCtx, newName, newHandler);
    }

    private void doAddBefore(DefaultChannelHandlerContext oldCtx, String newName, ChannelHandler newHandler) {
        if (oldCtx == head) {
            addFirst(newName, newHandler);
            return;
        }
        checkDuplicateName(newName);
        DefaultChannelHandlerContext prev = oldCtx.prev;
        DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(prev, oldCtx, newName, newHandler);

        prev.next = newCtx;
        oldCtx.prev = newCtx;

        name2ctx.put(newName, newCtx);
    }

    @Override
    public void addAfter(String oldName, String newName, ChannelHandler newHandler) {
        DefaultChannelHandlerContext oldCtx = getContextOrException(oldName);
        doAddAftert(oldCtx, newName, newHandler);
    }

    @Override
    public void addAfter(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {
        DefaultChannelHandlerContext oldCtx = getContextOrException(oldHandler);
        doAddAftert(oldCtx, newName, newHandler);
    }

    private void doAddAftert(DefaultChannelHandlerContext oldCtx, String newName, ChannelHandler newHandler) {
        if (oldCtx == tail) {
            addLast(newName, newHandler);
            return;
        }
        checkDuplicateName(newName);
        DefaultChannelHandlerContext next = oldCtx.next;
        DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(oldCtx, next, newName, newHandler);

        next.prev = newCtx;
        oldCtx.next = newCtx;

        name2ctx.put(newName, newCtx);
    }

    @Override
    public ChannelHandler remove(String name) {

        return remove(getContextOrException(name)).getHandler();

    }

    @Override
    public void remove(ChannelHandler handler) {
        remove(getContextOrException(handler));
    }

    private DefaultChannelHandlerContext remove(DefaultChannelHandlerContext ctx) {

        if (ctx == head) {
            //移除头部
            removeFirst();
        } else if (ctx == tail) {
            removeLast();
        }
        DefaultChannelHandlerContext prev = ctx.prev;
        DefaultChannelHandlerContext next = ctx.next;

        prev.next = next;
        next.prev = prev;

        name2ctx.remove(ctx.getName());
        return ctx;
    }


    @Override
    public void replace(String oldName, String newName, ChannelHandler newHandler) {

    }

    @Override
    public void replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler) {

    }

    @Override
    public void sendDownstream(ChannelEvent e) {
        DefaultChannelHandlerContext tail = getAcutalDownstreamContext(this.tail);
        if (tail == null) {
            //事件下沉
            getSink().eventSunk(this,e);
        } else {
            sendDownstream(tail, e);
        }
    }

    @Override
    public void sendUpstream(ChannelEvent e) {
        DefaultChannelHandlerContext head = getAcutalUpstreamContext(this.head);
        if (head != null) {
            sendUpstream(head, e);
        }
    }

    @Override
    public ChannelSink getSink() {
        return sink;
    }

    @Override
    public Channel getChannel() {
        return channel;
    }

    @Override
    public void attach(Channel channel, ChannelSink sink) {
        if(this.channel != null){
            throw new IllegalArgumentException("channle has already exist");
        }
        if(this.sink != null){
            throw new IllegalArgumentException("channel sink has already exist");
        }
        this.channel = channel;
        this.sink = sink;
    }


    private void sendUpstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        ((ChannelUpstreamHandler) ctx.handler).handleUpstream(ctx, e);
    }

    private void sendDownstream(DefaultChannelHandlerContext ctx, ChannelEvent e) {
        ((ChannelDownstreamHandler) ctx.handler).handleDownstream(ctx, e);
    }

    private DefaultChannelHandlerContext getAcutalUpstreamContext(DefaultChannelHandlerContext ctx) {
        if(ctx == null){
            return null;
        }

        DefaultChannelHandlerContext realCtx = ctx;
        while (!realCtx.canHandleUpstream()) {
            realCtx = realCtx.next;
            if (realCtx == null) {
                break;
            }
        }
        return realCtx;
    }

    //检测是否已经存在相同的handler name,存在则向上抛出异常
    private void checkDuplicateName(String name) {
        if (name2ctx.containsKey(name)) {
            throw new IllegalArgumentException("Duplicate handler name: " + name);
        }
    }

    private DefaultChannelHandlerContext getAcutalDownstreamContext(DefaultChannelHandlerContext ctx) {
        if(ctx == null){
            return null;
        }
        //由重构这本书说不建议使用用局部变量ctx参数进行赋予值,而是重新定一个realCtx
        DefaultChannelHandlerContext realCtx = ctx;
        //从链表尾部往前遍历
        while (!realCtx.canHandleDownstream()) {
            realCtx = realCtx.prev;
            if (realCtx == null) {
                break;
            }
        }
        return realCtx;
    }

    private ChannelHandler removeFirst() {
        if (name2ctx.isEmpty()) {
            throw new NoSuchElementException();
        }

        DefaultChannelHandlerContext oldHead = head;
        if (oldHead == null) {
            throw new NoSuchElementException();
        }


        if (oldHead.next == null) {
            head = tail = null;
            name2ctx.clear();
        } else {
            oldHead.next.prev = null;
            head = oldHead.next;
            name2ctx.remove(oldHead.getName());
        }

        return oldHead.getHandler();
    }

    private ChannelHandler removeLast() {
        if (name2ctx.isEmpty()) {
            throw new NoSuchElementException();
        }

        DefaultChannelHandlerContext oldTail = tail;
        if (oldTail == null) {
            throw new NoSuchElementException();
        }

        if (oldTail.prev == null) {
            head = tail = null;
            name2ctx.clear();
        } else {
            tail = oldTail.prev;
            oldTail.prev.next = null;
            name2ctx.remove(oldTail.getName());
        }


        return oldTail.getHandler();
    }

    private DefaultChannelHandlerContext getContextOrException(String name) {
        DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(name);

        if (ctx == null) {
            throw new NoSuchElementException(name);
        } else {
            return ctx;
        }
    }

    private DefaultChannelHandlerContext getContextOrException(ChannelHandler handler) {
        DefaultChannelHandlerContext ctx = (DefaultChannelHandlerContext) getContext(handler);

        if (ctx == null) {
            throw new NoSuchElementException(handler.getClass().getName());
        } else {
            return ctx;
        }
    }


    /**
     * 尽早发现异常代价就会很小，并向上抛出
     *
     * @param name
     * @return
     */
    public ChannelHandlerContext getContext(String name) {
        if (name == null) {
            throw new NullPointerException("name");
        }
        return name2ctx.get(name);
    }

    private ChannelHandlerContext getContext(ChannelHandler handler) {
        if (handler == null) {
            throw new NullPointerException("handler");
        }

        DefaultChannelHandlerContext ctx = head;
        while (ctx != null) {
            if (ctx.getHandler() == handler) {
                return ctx;
            }
            ctx = ctx.next;
        }
        return null;
    }

    private void init(String name, ChannelHandler handler) {
        DefaultChannelHandlerContext ctx = new DefaultChannelHandlerContext(null, null, name, handler);
        head = tail = ctx;
        name2ctx.clear();
        name2ctx.put(name, ctx);
    }

    @Override
    public String toString() {
        System.out.println("names" + name2ctx.keySet());
        StringBuilder builder = new StringBuilder();
        DefaultChannelHandlerContext head = this.head;
       /* for(;;){
            //TODO 如何判断head 是否为null 看源码如何实现的？
            if(head != null) {
                builder.append(head);
                head = head.next;
            }else{
                break;
            }
        }*/
        while (head != null) {
            builder.append(head);
            head = head.next;
        }

        return builder.toString();
    }

    private class DefaultChannelHandlerContext implements ChannelHandlerContext {

        private String name;
        private ChannelHandler handler;
        private DefaultChannelHandlerContext prev;
        private DefaultChannelHandlerContext next;

        private boolean canHandleDownstream;
        private boolean canHandleUpstream;

        public DefaultChannelHandlerContext(DefaultChannelHandlerContext prev, DefaultChannelHandlerContext next, String name, ChannelHandler handler) {
            this.prev = prev;
            this.next = next;
            this.name = name;
            this.handler = handler;

            canHandleDownstream = (handler instanceof ChannelDownstreamHandler);
            canHandleUpstream = (handler instanceof ChannelUpstreamHandler);
        }

        @Override
        public String getName() {
            return name;
        }

        @Override
        public ChannelHandler getHandler() {
            return handler;
        }

        /**
         * 该方法会被在handler中调用，并且将该事件传给该context之前的一个context并存放ChannelDownstreamHandler发送该事件
         *
         * @param e
         */
        @Override
        public void sendDownstream(ChannelEvent e) {
            DefaultChannelHandlerContext ctx = getAcutalDownstreamContext(this.prev);
            if (ctx == null) {
                //事件下沉
                getSink().eventSunk(DefaultChannelPipeline.this,e);
            } else {
                DefaultChannelPipeline.this.sendDownstream(ctx, e);
            }
        }

        @Override
        public void sendUpstream(ChannelEvent e) {
            //将事件传递到下个handler进行处理
            DefaultChannelHandlerContext ctx = getAcutalUpstreamContext(this.next);
            //TODO ctx判断是否为null
            if(ctx != null) {
                DefaultChannelPipeline.this.sendUpstream(ctx, e);
            }
        }

        @Override
        public boolean canHandleUpstream() {
            return canHandleUpstream;
        }

        @Override
        public boolean canHandleDownstream() {
            return canHandleDownstream;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();

            if (prev != null) {
                builder.append(" {prev=" + prev.name + ",");
            } else {
                builder.append(" {prev=null,");
            }
            builder.append(" name=" + name + ",");
            if (next != null) {
                builder.append(" next=" + next.name + "}");
            } else {
                builder.append(" next=null}");
            }
            return builder.toString();
        }
    }

}
